/**
 * 
 */
package com.google.code.jkippt;

import java.util.Iterator;

import com.google.code.jkippt.annotation.Experimental;
import com.google.code.jkippt.auth.Authentication;
import com.google.code.jkippt.http.APIClient;
import com.google.code.jkippt.http.EndPointFactory;
import com.google.code.jkippt.http.paging.PageIterator;
import com.google.code.jkippt.http.paging.PagedResultItemIterator;
import com.google.code.jkippt.parser.PageParserBuilder;
import com.google.code.jkippt.parser.Parser;
import com.google.code.jkippt.query.ClipQuery;
import com.google.code.jkippt.query.SearchQuery;
import com.google.code.jkippt.resource.Account;
import com.google.code.jkippt.resource.Clip;
import com.google.code.jkippt.resource.ClipList;

/**
 * The entry point for the API interaction.
 * @author Federico De Faveri defaveri@gmail.com
 */
public class JKippt {

	protected Authentication authentication;
	protected APIClient client;
	protected Parser parser;
	protected EndPointFactory endPointFactory;

	/**
	 * Creates a new {@link JKippt} class.
	 * @param authentication the authentication to use.
	 */
	public JKippt(Authentication authentication)
	{
		this.authentication = authentication;
		client = new APIClient(authentication);
		parser = new Parser();
		endPointFactory = EndPointFactory.getInstance();
	}

	/**
	 * Returns the user account.
	 * @return the user account.
	 * @throws JKipptException if an error occurs.
	 */
	public Account getAccount() throws JKipptException
	{
		String endPoint = endPointFactory.getAccountEndPoint();
		String json = client.get(endPoint);
		return parser.parseAccount(json);
	}

	/**
	 * Returns the specified {@link ClipList}.
	 * @param id the list id.
	 * @return the retrieved {@link ClipList}.
	 * @throws JKipptException if an error occurs.
	 */
	public ClipList getList(String id) throws JKipptException
	{
		String endPoint = endPointFactory.getListEndPoint(id);
		String json = client.get(endPoint);
		return parser.parse(json, ClipList.class);
	}
	
	/**
	 * Retrieves all available List.
	 * @return an {@link Iterator} over the {@link ClipList} set.
	 * @throws JKipptException if an error occurs.
	 */
	public Iterator<ClipList> getLists() throws JKipptException
	{
		PageParserBuilder<ClipList> parserBuilder = parser.getPageParserBuilder(ClipList.class);
		String firstPageEndPoint = endPointFactory.getListEndPoint();
		PageIterator<ClipList> pager = new PageIterator<ClipList>(client, parserBuilder, firstPageEndPoint);
		return new PagedResultItemIterator<ClipList>(pager);
	}
	
	/**
	 * Adds a new {@link ClipList}.
	 * @param title the list title.
	 * @return the created {@link ClipList}.
	 * @throws JKipptException if an error occurs.
	 */
	public ClipList addList(String title) throws JKipptException
	{
		ClipList list = new ClipList(title);
		String json = parser.toJson(list);
		String endPoint = endPointFactory.getListEndPoint();
		String newJson = client.post(endPoint, json);
		return parser.parse(newJson, ClipList.class);
	}
	
	/**
	 * Adds a new {@link ClipList}.
	 * @param title the list title.
	 * @param description the list description.
	 * @return the created {@link ClipList}.
	 * @throws JKipptException if an error occurs.
	 */
	@Experimental
	public ClipList addList(String title, String description) throws JKipptException
	{
		ClipList list = new ClipList(title, description);
		String json = parser.toJson(list);
		String endPoint = endPointFactory.getListEndPoint();
		String newJson = client.post(endPoint, json);
		return parser.parse(newJson, ClipList.class);
	}
	
	/**
	 * Updates the given list.
	 * @param list the {@link ClipList} to update.
	 * @return the updated {@link ClipList}.
	 * @throws JKipptException if an error occurs.
	 */
	public ClipList updateList(ClipList list) throws JKipptException
	{
		String json = parser.toJson(list);
		String endPoint = endPointFactory.getListEndPoint(list.getId());
		String newJson = client.put(endPoint, json);
		return parser.parse(newJson, ClipList.class);
	}
	
	/**
	 * Deletes the specified {@link ClipList}.
	 * @param id the List id to remove.
	 * @throws JKipptException if an error occurs.
	 */
	public void deleteList(String id) throws JKipptException
	{
		String endPoint = endPointFactory.getListEndPoint(id);
		client.delete(endPoint);
	}
	
	/**
	 * Adds a new {@link Clip} with the specified url.
	 * @param url the {@link Clip} url.
	 * @return the created {@link Clip}.
	 * @throws JKipptException if an error occurs.
	 */
	public Clip addClip(String url) throws JKipptException
	{
		Clip clip = new Clip(url);
		String json = parser.toJson(clip);
		String endPoint = endPointFactory.getClipEndPoint();
		String newJson = client.post(endPoint, json);
		return parser.parse(newJson, Clip.class);
	}
	
	/**
	 * Adds a new {@link Clip}. The {@link Clip} have to be new.
	 * @param clip the {@link Clip} to add.
	 * @return the added {@link Clip}.
	 * @throws JKipptException
	 */
	public Clip addClip(Clip clip) throws JKipptException
	{
		if (clip.getId()!=null) throw new IllegalArgumentException("The passed clip has already an id, you can only update it");
		String json = parser.toJson(clip);
		String endPoint = endPointFactory.getClipEndPoint();
		String newJson = client.post(endPoint, json);
		return parser.parse(newJson, Clip.class);
	}
	
	/**
	 * Updates the specified {@link Clip}.
	 * @param clip the {@link Clip} to update.
	 * @return the updated {@link Clip}.
	 * @throws JKipptException
	 */
	public Clip updateClip(Clip clip) throws JKipptException
	{
		String json = parser.toJson(clip);
		String endPoint = endPointFactory.getClipEndPoint(clip.getId());
		String newJson = client.put(endPoint, json);
		return parser.parse(newJson, Clip.class);
	}
	
	/**
	 * Gets the specified {@link Clip}.
	 * @param id the {@link Clip} id.
	 * @return the retrieved {@link Clip}.
	 * @throws JKipptException if an error occurs.
	 */
	public Clip getClip(String id) throws JKipptException
	{
		String endPoint = endPointFactory.getClipEndPoint(id);
		String json = client.get(endPoint);
		return parser.parse(json, Clip.class);
	}
	
	/**
	 * Deletes the specified {@link Clip}.
	 * @param id the {@link Clip} id.
	 * @throws JKipptException if an error occurs.
	 */
	public void deleteClip(String id) throws JKipptException
	{
		String endPoint = endPointFactory.getClipEndPoint(id);
		client.delete(endPoint);
	}

	/**
	 * Gets all available {@link Clip}s.
	 * @return the retrieved {@link Clip}s
	 * @throws JKipptException if an error occurs.
	 */
	public Iterator<Clip> getClips() throws JKipptException
	{
		PageParserBuilder<Clip> parserBuilder = parser.getPageParserBuilder(Clip.class);
		String firstPageEndPoint = endPointFactory.getClipEndPoint();
		PageIterator<Clip> pager = new PageIterator<Clip>(client, parserBuilder, firstPageEndPoint);
		return new PagedResultItemIterator<Clip>(pager);
	}
	
	/**
	 * Gets all available {@link Clip}s using the specified parameters.
	 * @param query the retrieving parameters.
	 * @return the retrieved {@link Clip}s.
	 * @throws JKipptException if an error occurs.
	 */
	public Iterator<Clip> getClips(ClipQuery query) throws JKipptException
	{
		PageParserBuilder<Clip> parserBuilder = parser.getPageParserBuilder(Clip.class);
		String firstPageEndPoint = endPointFactory.getClipEndPoint(query.getListId(), query.getSince());
		PageIterator<Clip> pager = new PageIterator<Clip>(client, parserBuilder, firstPageEndPoint);
		return new PagedResultItemIterator<Clip>(pager);
	}
	
	/**
	 * Search the available {@link Clip}s using the specified query.
	 * @param query the search term.
	 * @return the retrieved results.
	 * @throws JKipptException if an error occurs.
	 */
	public Iterator<Clip> searchClips(String query) throws JKipptException
	{
		PageParserBuilder<Clip> parserBuilder = parser.getPageParserBuilder(Clip.class);
		String firstPageEndPoint = endPointFactory.getSearchEndPoint(query);
		PageIterator<Clip> pager = new PageIterator<Clip>(client, parserBuilder, firstPageEndPoint);
		return new PagedResultItemIterator<Clip>(pager);
	}
	
	/**
	 * Search the available {@link Clip}s using the specified parameters.
	 * @param query the retrieving parameters.
	 * @return the query result.
	 * @throws JKipptException if an error occurs.
	 */
	public Iterator<Clip> searchClips(SearchQuery query) throws JKipptException
	{
		PageParserBuilder<Clip> parserBuilder = parser.getPageParserBuilder(Clip.class);
		String firstPageEndPoint = endPointFactory.getSearchEndPoint(query.getQuery(), query.getListId(), query.getIsStarred());
		PageIterator<Clip> pager = new PageIterator<Clip>(client, parserBuilder, firstPageEndPoint);
		return new PagedResultItemIterator<Clip>(pager);
	}

}
